import org.junit.jupiter.api.Test;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Comparator;

/**
 * OffsetDateTime类用于表示偏离UTC的日期时间，由dateTime和offset两个变量表示。其中dateTime是LocalDateTime类的对象，表示日期时间，
 * offset是ZoneOffset类的对象，用于表示dateTime对象相对于UTC的偏移量。
 * ”+08:00“表示比UTC时间快8小时，”-08:00“表示比UTC时间慢8小时。
 */
public class OffsetDateTimeDemo {

    /**
     * MAX：offsetDateTime 对象能表示的最大值。
     * MIN：offsetDateTime 对象能表示的最小值。
     */
    @Test
    public void test01(){
        System.out.println(OffsetDateTime.MAX); // 输出 +999999999-12-31T23:59:59.999999999-18:00
        System.out.println(OffsetDateTime.MIN); // 输出 -999999999-01-01T00:00+18:00
    }
}

class OffsetDateTimeDemo01 {
    /**
     * from(TemporalAccessor temporal)
     * 作用：从其他具有偏移量的时间类获取 offsetDateTime 对象。
     */
    @Test
    public void test01(){
        ZonedDateTime zonedDateTime01 = ZonedDateTime.parse("2023-01-01T08:00:00+08:00[Asia/Shanghai]");
        OffsetDateTime offsetDateTime01 = OffsetDateTime.from(zonedDateTime01);
        System.out.println(offsetDateTime01); // 输出 2023-01-01T08:00+08:00
    }

    /**
     * now()
     * 作用：从操作系统获取 offsetDateTime 对象。
     *
     * now(ZoneId zone)
     * 作用：获取指定时区此刻的日期时间。
     *
     * now(Clock clock)
     * 作用：从 clock 对象获取 offsetDateTime 对象。clock 对象有时区的概念，
     *      获取的是 clock 对象指定时区的日期时间。
     */
    @Test
    public void test02(){
        OffsetDateTime offsetDateTime01 = OffsetDateTime.now();
        System.out.println(offsetDateTime01); // 输出 2023-05-21T10:59:49.191+08:00（当前系统时间 2023-05-21T10:59:49）

        ZoneId zoneId01 = ZoneId.of("America/Los_Angeles");
        OffsetDateTime offsetDateTime02 = OffsetDateTime.now(zoneId01);
        System.out.println(offsetDateTime02); // 输出 2023-05-20T19:59:49.193-07:00

        Instant instant01 = Instant.parse("2023-04-01T10:00:00Z");
        Clock fixedClock01 = Clock.fixed(instant01, zoneId01);
        OffsetDateTime offsetDateTime03 = OffsetDateTime.now(fixedClock01);
        System.out.println(offsetDateTime03); // 输出 2023-04-01T03:00-07:00
    }

    /**
     * of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneOffset offset)
     * of(LocalDate date, LocalTime time, ZoneOffset offset)
     * of(LocalDateTime dateTime, ZoneOffset offset)
     * ofInstant(Instant instant, ZoneId zone)
     * 作用：根据年月日时分秒以及偏移量数据创建 offsetDateTime 对象。
     */
    @Test
    public void test03(){
        ZoneOffset zoneOffset01 = ZoneOffset.of("+08:00");
        OffsetDateTime offsetDateTime01 = OffsetDateTime.of(2023, 1, 1, 12, 30, 45, 100, zoneOffset01);
        System.out.println(offsetDateTime01); // 输出 2023-01-01T12:30:45.000000100+08:00

        LocalDateTime dateTime01 = LocalDateTime.parse("2023-01-01T12:30:45.000000100");
        OffsetDateTime offsetDateTime02 = OffsetDateTime.of(dateTime01, zoneOffset01);
        System.out.println(offsetDateTime02); // 输出 2023-01-01T12:30:45.000000100+08:00

        Instant instant01 = Instant.parse("2023-01-01T12:30:45.000000100Z");
        OffsetDateTime offsetDateTime03 = OffsetDateTime.ofInstant(instant01, zoneOffset01);
        System.out.println(offsetDateTime03); // 输出 2023-01-01T20:30:45.000000100+08:00
    }

    /**
     * parse(CharSequence text)
     * parse(CharSequence text, DateTimeFormatter formatter)
     * 作用：将字符串解析成 offsetDateTime 对象，默认格式 yyyy-MM-ddTHH:mm:ssxxx，
     *      其中xxx表示偏移量，formatter用来指定字符串格式。
     */
    @Test
    public void test04(){
        OffsetDateTime offsetDateTime01 = OffsetDateTime.parse("2023-01-01T12:30:45.100+08:00");
        System.out.println(offsetDateTime01); // 输出 2023-01-01T12:30:45.100+08:00

        DateTimeFormatter df01 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒 偏移量xxx");
        OffsetDateTime offsetDateTime02 = OffsetDateTime.parse("2023年01月01日 12时30分45秒 偏移量+08:00", df01);
        System.out.println(offsetDateTime02); // 输出 2023-01-01T12:30:45+08:00
    }

    /**
     * timeLineOrder()
     * 作用：获得一个比较器，比较器用来比较两个 offsetDateTime 对象的大小。
     *      比较逻辑是将两个 offsetDateTime 对象转换成UTC时间然后比较大小。
     */
    @Test
    public void test05(){
        Comparator<OffsetDateTime> comparator01 = OffsetDateTime.timeLineOrder();
        OffsetDateTime dateTime01 = OffsetDateTime.parse("2023-01-01T08:00:00+08:00"); // UTC时间 2023-01-01T00:00:00
        OffsetDateTime dateTime02 = OffsetDateTime.parse("2023-01-01T07:00:00+07:00"); // UTC时间 2023-01-01T00:00:00
        System.out.println(comparator01.compare(dateTime01, dateTime02)); // 输出 0
    }
}

class OffsetDateTimeDemo02 {

    /**
     * format(DateTimeFormatter formatter)
     * 作用：将 offsetDateTime 对象按指定格式输出。
     */
    @Test
    public void test01(){
        OffsetDateTime dateTime01 = OffsetDateTime.parse("2023-01-01T08:00:00+08:00"); // UTC时间 2023-01-01T00:00:00
        DateTimeFormatter df01 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:ss:mm xxx");
        System.out.println(dateTime01.format(df01)); // 输出 2023/01/01 08:00:00 +08:00
    }

    /**
     * getOffset()
     * 作用：获取偏移量。
     */
    @Test
    public void test02() {
        OffsetDateTime dateTime01 = OffsetDateTime.parse("2023-01-01T08:00:00+08:00"); // UTC时间 2023-01-01T00:00:00
        ZoneOffset offset01 = dateTime01.getOffset();
        System.out.println(offset01); // 输出 +08:00
    }

    /**
     * toEpochSecond()
     * 作用：将 offsetDateTime 对象转成 UTC 时间，
     *      然后计算距离 EPOCH 的秒数。
     *
     * toInstant()
     * toLocalDate()
     * toLocalDateTime()
     * toLocalTime()
     * toOffsetTime()
     * toZonedDateTime()
     * 作用：见名知意，通过 offsetDateTime 对象获取其他时间类对象。
     */
    @Test
    public void test03(){
        OffsetDateTime dateTime01 = OffsetDateTime.parse("1970-01-01T08:00:00+08:00"); // UTC时间 1970-01-01T00:00:00
        long epochSecond = dateTime01.toEpochSecond();
        System.out.println(epochSecond); // 输出 0

        Instant instant = dateTime01.toInstant();
        System.out.println(instant); // 输出 1970-01-01T00:00:00Z

        LocalDateTime localDateTime = dateTime01.toLocalDateTime();
        System.out.println(localDateTime); // 输出 1970-01-01T08:00

        OffsetTime offsetTime = dateTime01.toOffsetTime();
        System.out.println(offsetTime); // 输出 08:00+08:00
    }

    /**
     * truncatedTo(TemporalUnit unit)
     * 作用：根据时间单位截取 offsetDateTime 对象。
     */
    @Test
    public void test04(){
        OffsetDateTime dateTime01 = OffsetDateTime.parse("2023-01-01T12:30:45.100+08:00");// 输出 2023-01-01T12:30:45.100+08:00
        OffsetDateTime dateTime02 = dateTime01.truncatedTo(ChronoUnit.MINUTES);
        OffsetDateTime dateTime03 = dateTime01.truncatedTo(ChronoUnit.HOURS);
        System.out.println(dateTime01); // 输出 2023-01-01T12:30:45.100+08:00
        System.out.println(dateTime02); // 输出 2023-01-01T12:30+08:00
        System.out.println(dateTime03); // 输出 2023-01-01T12:00+08:00
    }

    /**
     * until(Temporal endExclusive, TemporalUnit unit)
     * 作用：根据指定的时间单位计算两个 offsetDateTime 对象的差值。
     */
    @Test
    public void test05(){
        OffsetDateTime dateTime01 = OffsetDateTime.parse("1970-01-01T08:00:00+08:00"); // UTC时间 1970-01-01T00:00:00
        OffsetDateTime dateTime02 = OffsetDateTime.parse("1970-01-01T09:00:00+08:00"); // UTC时间 1970-01-01T01:00:00
        System.out.println(dateTime01.until(dateTime02, ChronoUnit.HOURS)); // 输出 1
        System.out.println(dateTime01.until(dateTime02, ChronoUnit.MINUTES)); // 输出 60
    }

    /**
     * withOffsetSameInstant(ZoneOffset offset)
     * 作用：通过调整偏移量来调整 offsetDateTime 对象，
     *      但不管如何调整，转换成的 UTC 时间不变。
     *
     * withOffsetSameLocal(ZoneOffset offset)
     * 作用：时间不变，仅改变偏移量。
     */
    @Test
    public void test06(){
        OffsetDateTime dateTime01 = OffsetDateTime.parse("1970-01-01T08:00:00+08:00"); // UTC时间 1970-01-01T00:00:00
        ZoneOffset zoneOffset01 = ZoneOffset.of("+09:00");
        ZoneOffset zoneOffset02 = ZoneOffset.of("-08:00");
        System.out.println(dateTime01.withOffsetSameInstant(zoneOffset01)); // 输出 1970-01-01T09:00+09:00（UTC时间 1970-01-01T00:00:00）
        System.out.println(dateTime01.withOffsetSameInstant(zoneOffset02)); // 输出 1969-12-31T16:00-08:00（UTC时间 1970-01-01T00:00:00）

        System.out.println(dateTime01.withOffsetSameLocal(zoneOffset01)); // 输出 1970-01-01T08:00+09:00
        System.out.println(dateTime01.withOffsetSameLocal(zoneOffset02)); // 输出 1970-01-01T08:00-08:00
    }

    /**
     * atZoneSameInstant(ZoneId zone)
     * 作用：通过调整时区或偏移量来调整 offsetDateTime 对象，
     *      但不管如何调整，转换成的 UTC 时间不变。
     *
     * atZoneSimilarLocal(ZoneId zone)
     * 作用：时间不变，仅改变时区或偏移量。
     */
    @Test
    public void test07(){
        OffsetDateTime offsetDateTime01 = OffsetDateTime.parse("2023-01-01T18:00:00+08:00");
        ZoneId zoneId01 = ZoneId.of("America/Los_Angeles");
        ZonedDateTime zonedDateTime01 = offsetDateTime01.atZoneSameInstant(zoneId01);
        System.out.println(zonedDateTime01); // 输出 2023-01-01T02:00-08:00[America/Los_Angeles]

        ZonedDateTime zonedDateTime02 = offsetDateTime01.atZoneSimilarLocal(zoneId01);
        System.out.println(zonedDateTime02); // 输出 2023-01-01T18:00-08:00[America/Los_Angeles]
    }
}